package org.dubnation.auth.service.impl;

import org.dubnation.auth.domain.Permission;
import org.dubnation.auth.service.IPermissionScanService;
import org.dubnation.auth.service.IPermissionService;
import org.dubnation.base.annotation.DubnationPermission;
import org.dubnation.base.enums.RequestTypeEnum;
import org.dubnation.base.util.ClassUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Objects;

/**
 * 监听器的业务代码，扫描注解
 */
@Transactional
@Service
public class PermissionScanServiceImpl implements IPermissionScanService {

    @Value("${dubnation.permission.scan-packages}")
    private String url;

    @Autowired
    private IPermissionService permissionService;

    /*
    * 权限有类权限和接口权限，如果有接口权限则必须有类权限，有类权限不一定有接口权限
    * */
    @Override
    public void scan() {
        //清空权限表
        permissionService.deleteAll(); //每次启动扫描的时候都先清空表，然后再新增
        List<Class> allClassName = ClassUtils.getAllClassName(url); //获取到每个Controller类的字节码对象
        //遍历集合
        for (Class clazz : allClassName) {
            //1.判断controller是否权限
            DubnationPermission clazzAnnotation = (DubnationPermission) clazz.getAnnotation(DubnationPermission.class); //获取到每一个Controller类上的DubnationPermission注解
            if (Objects.isNull(clazzAnnotation)){
                //该Controller类上没有DubnationPermission注解，即没有权限
                continue;//跳过本次循环判断下一个类
            }
            //封装父权限入库
            Permission parentPermission = new Permission();
            parentPermission.setName(clazzAnnotation.name());
            parentPermission.setDescs(clazzAnnotation.desc());
            parentPermission.setSn(clazz.getSimpleName());
            //获取父级权限的url
            RequestMapping clazzAnnotation1 = (RequestMapping) clazz.getAnnotation(RequestMapping.class);
            String parentUrl = clazzAnnotation1.value()[0]; //本来是得到一个url的String数组，但是该注解中只有一个value
            parentPermission.setUrl(parentUrl);
            parentPermission.setParent(null);
            permissionService.insert(parentPermission);

            //2.如果controller有权限，遍历获取所有的方法，判断每一个方法是否有权限
            Method[] clazzMethods = clazz.getMethods();//获取类的所有方法
            for (Method clazzMethod : clazzMethods) {
                DubnationPermission methodAnnotation = clazzMethod.getAnnotation(DubnationPermission.class);
                if (Objects.isNull(methodAnnotation)){
                    //该方法没有DubnationPermission注解
                    continue;//判断下一个方法
                }

                //3.如果方法需要权限，获取注解，封装权限对象入库
                Permission permission = new Permission();
                permission.setDescs(methodAnnotation.desc());
                permission.setName(methodAnnotation.name());
                permission.setSn(clazz.getSimpleName() + ":" + clazzMethod.getName()); //controller名 + 方法名
                /*
                * 获取每个方法资源路径url
                * */
                String methodUrl = getMethodUrl(clazzMethod);
                permission.setUrl(parentUrl + methodUrl); //controller路径 + 方法路径
                permission.setParent(parentPermission); //父权限
                permissionService.insert(permission); //权限入库（父权限入库后获取主键，父权限就有了id）
            }
        }
    }

    /*
    * 用来获取接口的资源路径
    * */
    public String getMethodUrl(Method method){
        String methodUrl = "";
        // 取枚举中定义的所有的请求类型
        RequestTypeEnum[] requestTypeEnums = RequestTypeEnum.values();
        for (RequestTypeEnum typeEnum : requestTypeEnums) {
            // 根据枚举中的不同的请求类型的class获取该类型对应的注解对象
            Annotation annotation = method.getAnnotation(typeEnum.getRequestType());
            // 如果该请求类型注解不存在就跳过
            if(Objects.isNull(annotation)){
                continue;
            }
            try {
                // 如果该请求类型注解存在,就获取它里面的value方法
                Class<? extends Annotation> aClass = annotation.annotationType();
                Method annotationMethod = aClass.getMethod("value");
                // 通过invoke调用该对象的value方法,获取结果
                // 反射执行 方法 方法名.invoke(对象)
                String[] value = (String[]) annotationMethod.invoke(annotation);
                // 如果value不为空且长度大于0,就赋值给methodUrl,并跳出循环
                if(value != null && value.length > 0){
                    methodUrl = value[0];
                    break;
                }
            }catch (Exception e) {
                e.printStackTrace();
                continue;
            }
        }
        return methodUrl;

    }
}
